#include "camera.h"
#include "vector.h"
#include <cmath>

Camera::Camera(){
    eye = Vector3D(0, -5, 5);
    lookAt = Vector3D(0, 0, 0);
    up = Vector3D(0, 0, 1);

    m = 400;
    n = 300;
    f = 3.0;
    setMatrices();
    discretization = 100;
}

Camera::Camera(unsigned int _m, unsigned int _n, double _f)
    : m(_m), n(_n), f(_f){
    eye = Vector3D(0, -5, 5);
    lookAt = Vector3D(0, 0, 0);
    up = Vector3D(0, 0, 1);
    setMatrices();
    discretization = 100;
}

Camera::Camera(const Vector3D& _eye, const Vector3D& _lookAt, const Vector3D& _up, unsigned int _m, unsigned int _n, double _f)
    : eye(_eye), lookAt(_lookAt), up(_up), m(_m), n(_n), f(_f){
    setMatrices();
}

Camera::~Camera(){
}

Ray Camera::getRay(unsigned int _i, unsigned int _j){
    Vector3D u, v, w;
    getLocalCoordinates(&u, &v, &w);

    Vector3D d = ((u * (-((_i - floor(n/2.0))/discretization))) + (v * ((_j - floor(m/2.0))/discretization)) + (w * f));
//
//
//    Matrix toWorldDirection = transform.getLocalToWorldDirection();
//    d = Vector3D(toWorldDirection * Vector(d));
//
//            std::cout <<"\n- ORIGEM DO RAIO" <<std::endl;
//            eye.print();
//            std::cout <<"\n- DIREÇÂO" << std::endl;
//            d.normalize().print();

    return Ray(eye, (d).normalize());
}

void Camera::getLocalCoordinates(Vector3D *_u, Vector3D *_v, Vector3D *_w) const{
    *_w = (lookAt - eye).normalize();

    *_v = (_w->crossProduct(up).normalize());

    *_u = _v->crossProduct(*_w);
}

void Camera::setMatrices(){
    Vector3D u, v, w;
    getLocalCoordinates(&u, &v, &w);

    Matrix localToWorld = Matrix(4,4);

    for(int i = 0; i < 3; i++){
        localToWorld[i][0] = u[i];
        localToWorld[i][1] = v[i];
        localToWorld[i][2] = w[i];
        localToWorld[i][3] = eye[i];
    }
    localToWorld[3][3] = 1;


    Matrix worldToLocal = localToWorld.transpose();
    for(int i = 0; i < 3; i++){
        worldToLocal[3][i] = 0;
    }
    worldToLocal[0][3] = -u.dotProduct(eye);
    worldToLocal[1][3] = -v.dotProduct(eye);
    worldToLocal[2][3] = -w.dotProduct(eye);

    transform.setLocalToWorld(localToWorld);
    transform.setWorldToLocal(worldToLocal);
}

void Camera::draw() const{
//    std::cout <<"Desenhando camera" << std::endl;

    Vector3D u, v, w;
    getLocalCoordinates(&u, &v, &w);


    glPointSize(2);
    glColor3f(1.0, 1.0, 0.2);

    double i = 0;
    double j = 0;

    Vector3D p0, p1, p2, p3;

    Vector3D d = ((u * (-((i - floor(n/2.0))/discretization))) + (v * ((j - floor(m/2.0))/discretization)) + (w * f));
    p0 = (eye+d);

    j = n - 1;
    d = ((u * (-((i - floor(n/2.0))/discretization))) + (v * ((j - floor(m/2.0))/discretization)) + (w * f));
    p1 = (eye+d);

    i = n - 1;
    d = ((u * (-((i - floor(n/2.0))/discretization))) + (v * ((j - floor(m/2.0))/discretization)) + (w * f));
    p2 = (eye+d);

    j = 0;
    d = ((u * (-((i - floor(n/2.0))/discretization))) + (v * ((j - floor(m/2.0))/discretization)) + (w * f));
    p3 = (eye+d);

    Vector3D n = (p0 - p1).crossProduct(p2 - p1).normalize();

    glBegin(GL_QUADS);
        glNormal3dv((-n).intoVector());
        glVertex3dv(p0.intoVector());
        glVertex3dv(p1.intoVector());
        glVertex3dv(p2.intoVector());
        glVertex3dv(p3.intoVector());
    glEnd();

    n = (p0 - eye).crossProduct(p1 - eye).normalize();
    glBegin(GL_TRIANGLES);
        glNormal3dv((-n).intoVector());
        glVertex3dv(eye.intoVector());
        glVertex3dv(p1.intoVector());
        glVertex3dv(p0.intoVector());


    n = (p1 - eye).crossProduct(p2 - eye).normalize();

        glNormal3dv((-n).intoVector());
        glVertex3dv(eye.intoVector());
        glVertex3dv(p2.intoVector());
        glVertex3dv(p1.intoVector());


    n = (p2 - eye).crossProduct(p3 - eye).normalize();

        glNormal3dv((-n).intoVector());
        glVertex3dv(eye.intoVector());
        glVertex3dv(p3.intoVector());
        glVertex3dv(p2.intoVector());


    n = (p3 - eye).crossProduct(p0 - eye).normalize();

        glNormal3dv((-n).intoVector());
        glVertex3dv(eye.intoVector());
        glVertex3dv(p0.intoVector());
        glVertex3dv(p3.intoVector());
    glEnd();


    glPointSize(1);

    glColor3f(1.0, 1.0, 1.0);
}
